Passed
Push — master ( 05cadd...4c2765 )
by EMP
01:19
created

main.js ➔ clearDisplay   B

Complexity

Conditions 6

Size

Total Lines 11
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 9
dl 0
loc 11
rs 8.6666
c 0
b 0
f 0
1
"use strict";
2
3
sodium.ready.then(function() {
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
4
5
const ae = new AllEars(function(ok) {
1 ignored issue
show
Bug introduced by
The variable AllEars seems to be never declared. If this is a global, consider adding a /** global: AllEars */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
6
	if (ok) {
7
		const greeting = localStorage.greeting;
1 ignored issue
show
Bug introduced by
The variable localStorage seems to be never declared. If this is a global, consider adding a /** global: localStorage */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
8
		if (greeting) {
9
			document.getElementById("greeting").textContent = greeting;
10
			document.getElementById("txt_pg").value = greeting;
11
		}
12
13
		document.getElementById("txt_skey").style.background = "#466";
14
		document.getElementById("txt_skey").maxLength = "64";
15
	} else {
16
		console.log("Failed to load All-Ears");
17
	}
18
});
19
20
function TabState(cur, max, btnDele, btnUpdt) {
21
	this.cur = cur;
22
	this.max = max;
23
	this.btnDele = btnDele;
24
	this.btnUpdt = btnUpdt;
25
}
26
27
const tabs = [
28
	new TabState(0, 0, false, true), // Inbox
29
	new TabState(0, 0, false, true), // Outbx
30
	new TabState(0, 1, true, false), // Write
31
	new TabState(0, 2, false, false), // Notes
32
	new TabState(0, 3, false, true) // Tools
33
];
34
35
let showHeaders = false;
36
37
let tab = 0;
38
const TAB_INBOX = 0;
39
const TAB_DRBOX = 1;
40
const TAB_WRITE = 2;
41
const TAB_NOTES = 3;
42
const TAB_TOOLS = 4;
43
44
// Helper functions
45
function getCountryName(countryCode) {
46
	switch (countryCode) {
47
		case "??": return "Unknown";
48
		case "DZ": return "Algeria";
49
		case "AO": return "Angola";
50
		case "BJ": return "Benin";
51
		case "BW": return "Botswana";
52
		case "BF": return "Burkina Faso";
53
		case "BI": return "Burundi";
54
		case "CV": return "Cabo Verde";
55
		case "CM": return "Cameroon";
56
		case "CF": return "Central African Republic";
57
		case "TD": return "Chad";
58
		case "KM": return "Comoros";
59
		case "CD": return "Congo";
60
		case "DJ": return "Djibouti";
61
		case "EG": return "Egypt";
62
		case "GQ": return "Equatorial Guinea";
63
		case "ER": return "Eritrea";
64
		case "SZ": return "Eswatini";
65
		case "ET": return "Ethiopia";
66
		case "GA": return "Gabon";
67
		case "GM": return "Gambia";
68
		case "GH": return "Ghana";
69
		case "GW": return "Guinea-Bissau";
70
		case "GN": return "Guinea";
71
		case "CI": return "Ivory Coast";
72
		case "KE": return "Kenya";
73
		case "LS": return "Lesotho";
74
		case "LR": return "Liberia";
75
		case "LY": return "Libya";
76
		case "MG": return "Madagascar";
77
		case "MW": return "Malawi";
78
		case "ML": return "Mali";
79
		case "MR": return "Mauritania";
80
		case "MU": return "Mauritius";
81
		case "YT": return "Mayotte";
82
		case "MA": return "Morocco";
83
		case "MZ": return "Mozambique";
84
		case "NA": return "Namibia";
85
		case "NE": return "Niger";
86
		case "NG": return "Nigeria";
87
		case "CG": return "Republic of the Congo";
88
		case "RW": return "Rwanda";
89
		case "RE": return "Réunion";
90
		case "SH": return "Saint Helena";
91
		case "SN": return "Senegal";
92
		case "SC": return "Seychelles";
93
		case "SL": return "Sierra Leone";
94
		case "SO": return "Somalia";
95
		case "ZA": return "South Africa";
96
		case "SS": return "South Sudan";
97
		case "SD": return "Sudan";
98
		case "ST": return "São Tomé and Príncipe";
99
		case "TZ": return "Tanzania";
100
		case "TG": return "Togo";
101
		case "TN": return "Tunisia";
102
		case "UG": return "Uganda";
103
		case "EH": return "Western Sahara";
104
		case "ZM": return "Zambia";
105
		case "ZW": return "Zimbabwe";
106
		case "AQ": return "Antarctica";
107
		case "BV": return "Bouvet Island";
108
		case "TF": return "French Southern Territories";
109
		case "HM": return "Heard Island and McDonald Islands";
110
		case "GS": return "South Georgia and the South Sandwich Islands";
111
		case "AF": return "Afghanistan";
112
		case "AM": return "Armenia";
113
		case "AZ": return "Azerbaijan";
114
		case "BH": return "Bahrain";
115
		case "BD": return "Bangladesh";
116
		case "BT": return "Bhutan";
117
		case "IO": return "British Indian Ocean Territory";
118
		case "BN": return "Brunei";
119
		case "KH": return "Cambodia";
120
		case "CN": return "China";
121
		case "CC": return "Cocos [Keeling] Islands";
122
		case "GE": return "Georgia";
123
		case "JO": return "Hashemite Kingdom of Jordan";
124
		case "HK": return "Hong Kong";
125
		case "IN": return "India";
126
		case "ID": return "Indonesia";
127
		case "IR": return "Iran";
128
		case "IQ": return "Iraq";
129
		case "IL": return "Israel";
130
		case "JP": return "Japan";
131
		case "KZ": return "Kazakhstan";
132
		case "KW": return "Kuwait";
133
		case "KG": return "Kyrgyzstan";
134
		case "LA": return "Laos";
135
		case "LB": return "Lebanon";
136
		case "MO": return "Macao";
137
		case "MY": return "Malaysia";
138
		case "MV": return "Maldives";
139
		case "MN": return "Mongolia";
140
		case "MM": return "Myanmar";
141
		case "NP": return "Nepal";
142
		case "KP": return "North Korea";
143
		case "OM": return "Oman";
144
		case "PK": return "Pakistan";
145
		case "PS": return "Palestine";
146
		case "PH": return "Philippines";
147
		case "QA": return "Qatar";
148
		case "SA": return "Saudi Arabia";
149
		case "SG": return "Singapore";
150
		case "KR": return "South Korea";
151
		case "LK": return "Sri Lanka";
152
		case "SY": return "Syria";
153
		case "TW": return "Taiwan";
154
		case "TJ": return "Tajikistan";
155
		case "TH": return "Thailand";
156
		case "TR": return "Turkey";
157
		case "TM": return "Turkmenistan";
158
		case "AE": return "United Arab Emirates";
159
		case "UZ": return "Uzbekistan";
160
		case "VN": return "Vietnam";
161
		case "YE": return "Yemen";
162
		case "AL": return "Albania";
163
		case "AD": return "Andorra";
164
		case "AT": return "Austria";
165
		case "BY": return "Belarus";
166
		case "BE": return "Belgium";
167
		case "BA": return "Bosnia and Herzegovina";
168
		case "BG": return "Bulgaria";
169
		case "HR": return "Croatia";
170
		case "CY": return "Cyprus";
171
		case "CZ": return "Czechia";
172
		case "DK": return "Denmark";
173
		case "EE": return "Estonia";
174
		case "FO": return "Faroe Islands";
175
		case "FI": return "Finland";
176
		case "FR": return "France";
177
		case "DE": return "Germany";
178
		case "GI": return "Gibraltar";
179
		case "GR": return "Greece";
180
		case "GG": return "Guernsey";
181
		case "HU": return "Hungary";
182
		case "IS": return "Iceland";
183
		case "IE": return "Ireland";
184
		case "IM": return "Isle of Man";
185
		case "IT": return "Italy";
186
		case "JE": return "Jersey";
187
		case "XK": return "Kosovo";
188
		case "LV": return "Latvia";
189
		case "LI": return "Liechtenstein";
190
		case "LU": return "Luxembourg";
191
		case "MT": return "Malta";
192
		case "MC": return "Monaco";
193
		case "ME": return "Montenegro";
194
		case "NL": return "Netherlands";
195
		case "MK": return "North Macedonia";
196
		case "NO": return "Norway";
197
		case "PL": return "Poland";
198
		case "PT": return "Portugal";
199
		case "LT": return "Republic of Lithuania";
200
		case "MD": return "Republic of Moldova";
201
		case "RO": return "Romania";
202
		case "RU": return "Russia";
203
		case "SM": return "San Marino";
204
		case "RS": return "Serbia";
205
		case "SK": return "Slovakia";
206
		case "SI": return "Slovenia";
207
		case "ES": return "Spain";
208
		case "SJ": return "Svalbard and Jan Mayen";
209
		case "SE": return "Sweden";
210
		case "CH": return "Switzerland";
211
		case "UA": return "Ukraine";
212
		case "GB": return "United Kingdom";
213
		case "VA": return "Vatican City";
214
		case "AX": return "Åland";
215
		case "AI": return "Anguilla";
216
		case "AG": return "Antigua and Barbuda";
217
		case "AW": return "Aruba";
218
		case "BS": return "Bahamas";
219
		case "BB": return "Barbados";
220
		case "BZ": return "Belize";
221
		case "BM": return "Bermuda";
222
		case "BQ": return "Bonaire, Sint Eustatius, and Saba";
223
		case "VG": return "British Virgin Islands";
224
		case "CA": return "Canada";
225
		case "KY": return "Cayman Islands";
226
		case "CR": return "Costa Rica";
227
		case "CU": return "Cuba";
228
		case "CW": return "Curaçao";
229
		case "DM": return "Dominica";
230
		case "DO": return "Dominican Republic";
231
		case "SV": return "El Salvador";
232
		case "GL": return "Greenland";
233
		case "GD": return "Grenada";
234
		case "GP": return "Guadeloupe";
235
		case "GT": return "Guatemala";
236
		case "HT": return "Haiti";
237
		case "HN": return "Honduras";
238
		case "JM": return "Jamaica";
239
		case "MQ": return "Martinique";
240
		case "MX": return "Mexico";
241
		case "MS": return "Montserrat";
242
		case "NI": return "Nicaragua";
243
		case "PA": return "Panama";
244
		case "PR": return "Puerto Rico";
245
		case "BL": return "Saint Barthélemy";
246
		case "LC": return "Saint Lucia";
247
		case "MF": return "Saint Martin";
248
		case "PM": return "Saint Pierre and Miquelon";
249
		case "VC": return "Saint Vincent and the Grenadines";
250
		case "SX": return "Sint Maarten";
251
		case "KN": return "St Kitts and Nevis";
252
		case "TT": return "Trinidad and Tobago";
253
		case "TC": return "Turks and Caicos Islands";
254
		case "VI": return "U.S. Virgin Islands";
255
		case "US": return "United States";
256
		case "AS": return "American Samoa";
257
		case "AU": return "Australia";
258
		case "CX": return "Christmas Island";
259
		case "CK": return "Cook Islands";
260
		case "TL": return "Democratic Republic of Timor-Leste";
261
		case "FM": return "Federated States of Micronesia";
262
		case "FJ": return "Fiji";
263
		case "PF": return "French Polynesia";
264
		case "GU": return "Guam";
265
		case "KI": return "Kiribati";
266
		case "MH": return "Marshall Islands";
267
		case "NR": return "Nauru";
268
		case "NC": return "New Caledonia";
269
		case "NZ": return "New Zealand";
270
		case "NU": return "Niue";
271
		case "NF": return "Norfolk Island";
272
		case "MP": return "Northern Mariana Islands";
273
		case "PW": return "Palau";
274
		case "PG": return "Papua New Guinea";
275
		case "PN": return "Pitcairn Islands";
276
		case "WS": return "Samoa";
277
		case "SB": return "Solomon Islands";
278
		case "TK": return "Tokelau";
279
		case "TO": return "Tonga";
280
		case "TV": return "Tuvalu";
281
		case "UM": return "U.S. Minor Outlying Islands";
282
		case "VU": return "Vanuatu";
283
		case "WF": return "Wallis and Futuna";
284
		case "AR": return "Argentina";
285
		case "BO": return "Bolivia";
286
		case "BR": return "Brazil";
287
		case "CL": return "Chile";
288
		case "CO": return "Colombia";
289
		case "EC": return "Ecuador";
290
		case "FK": return "Falkland Islands";
291
		case "GF": return "French Guiana";
292
		case "GY": return "Guyana";
293
		case "PY": return "Paraguay";
294
		case "PE": return "Peru";
295
		case "SR": return "Suriname";
296
		case "UY": return "Uruguay";
297
		case "VE": return "Venezuela";
298
	}
299
300
	return "Error";
301
}
302
303
function getCountryFlag(countryCode) {
304
	return (!countryCode || countryCode.length !== 2 || countryCode == "??") ? "❔" : sodium.to_string(new Uint8Array([
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
305
		240, 159, 135, 166 + countryCode.codePointAt(0) - 65,
306
		240, 159, 135, 166 + countryCode.codePointAt(1) - 65
307
	]));
308
}
309
310
function getClockIcon(d) {
311
	const h24 = d.getUTCHours();
312
	let h12 = (h24 === 0 ? 12 : ((h24 > 12) ? h24 - 12 : h24));
313
314
	const m60 = (d.getUTCMinutes() * 60) + d.getUTCSeconds();
315
	let m30 = 0;
316
	if (m60 <= 900) { // <= 15: round down to this hour
317
		m30 = 0;
318
	} else if (m60 > 900 && m60 < 2700) { // 15..45: round to half-past this hour
319
		m30 = 12;
320
	} else { // >= 45: round up to next hour
321
		h12++;
322
		m30 = 0;
323
	}
324
325
	return "&#" + ((128335 + h12) + m30) + ";";
326
}
327
328
function clearDisplay() {
329
	let el = document.getElementById("midright").getElementsByTagName("img");
330
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("audio");
331
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("video");
332
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("embed");
333
	if (el.length !== 1) el = document.getElementById("midright").getElementsByTagName("iframe");
334
	if (el.length !== 1) return;
335
336
	URL.revokeObjectURL(el[0].src);
1 ignored issue
show
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
337
	el[0].remove();
338
}
339
340
function displayFile(num) {
341
	const fileType = ae.GetUplMsgType(num);
342
343
	if (!fileType) { // Download
344
		const a = document.createElement("a");
345
		a.href = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
2 ignored issues
show
Bug introduced by
The variable URL seems to be never declared. If this is a global, consider adding a /** global: URL */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable Blob seems to be never declared. If this is a global, consider adding a /** global: Blob */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
346
		a.download = ae.GetUplMsgTitle(num);
347
		a.click();
348
349
		URL.revokeObjectURL(a.href);
350
		a.href = "";
351
		a.download = "";
352
		return;
353
	}
354
355
	clearDisplay();
356
357
	document.getElementById("midright").scroll(0, 0);
358
	document.getElementById("midright").setAttribute("data-msgid", ae.GetUplMsgIdHex(num));
359
	document.getElementById("btn_reply").disabled = true;
360
	document.getElementById("btn_mdele").disabled = false;
361
	document.getElementById("midright").children[0].hidden = true;
362
	document.getElementById("midright").children[1].textContent = ae.GetUplMsgTitle(num);
363
364
	switch (fileType) {
365
		case "text": {
366
			document.getElementById("midright").children[2].hidden = false;
367
			document.getElementById("midright").children[2].textContent = sodium.to_string(ae.GetUplMsgBody(num));
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
368
		break;}
369
370
		case "image": {
371
			document.getElementById("midright").children[2].hidden = true;
372
			const img = document.createElement("img");
373
			img.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
374
			document.getElementById("midright").appendChild(img);
375
376
			img.onclick = function() {
377
				if (!document.fullscreen)
378
					img.requestFullscreen();
379
				else
380
					document.exitFullscreen();
381
			};
382
		break;}
383
384
		case "audio": {
385
			document.getElementById("midright").children[2].hidden = true;
386
			const el = document.createElement("audio");
387
			el.controls = "controls";
388
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
389
			document.getElementById("midright").appendChild(el);
390
		break;}
391
392
		case "video": {
393
			document.getElementById("midright").children[2].hidden = true;
394
			const el = document.createElement("video");
395
			el.controls = "controls";
396
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer]));
397
			document.getElementById("midright").appendChild(el);
398
		break;}
399
400
		case "pdf": {
401
			document.getElementById("midright").children[2].hidden = true;
402
			const el = document.createElement("embed");
403
			el.type = "application/pdf";
404
			el.src = URL.createObjectURL(new Blob([ae.GetUplMsgBody(num).buffer], {type: "application/pdf"}));
405
			document.getElementById("midright").appendChild(el);
406
		break;}
407
408
		case "html": {
409
			document.getElementById("midright").children[2].hidden = true;
410
			const el = document.createElement("iframe");
411
			el.allow = "";
412
			el.sandbox = "";
413
			el.csp = "base-uri 'none'; child-src 'none'; connect-src 'none'; default-src 'none'; font-src 'none'; form-action 'none'; frame-ancestors 'none'; frame-src 'none'; img-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; script-src 'none'; style-src 'none'; worker-src 'none';";
414
			el.srcdoc = sodium.to_string(ae.GetUplMsgBody(num).buffer);
415
			document.getElementById("midright").appendChild(el);
416
		break;}
417
	}
418
}
419
420
function displayMsg(isInt, num) {
421
	clearDisplay();
422
423
	document.getElementById("btn_mdele").disabled = false;
424
	document.getElementById("midright").scroll(0, 0);
425
	document.getElementById("midright").setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(num) : ae.GetExtMsgIdHex(num));
426
427
	const ts = isInt? ae.GetIntMsgTime(num) : ae.GetExtMsgTime(num);
428
429
	if (!isInt || (ae.GetIntMsgFrom(num) !== "public" && ae.GetIntMsgFrom(num) !== "system")) {
430
		document.getElementById("btn_reply").disabled = false;
431
432
		document.getElementById("btn_reply").onclick = function() {
433
			document.getElementById("write_recv").value = isInt? ae.GetIntMsgFrom(num) : ae.GetExtMsgReplyAddress(num);
434
			document.getElementById("write_subj").value = isInt? ae.GetIntMsgTitle(num) : ae.GetExtMsgTitle(num);
435
			if (!document.getElementById("write_subj").value.startsWith("Re:")) document.getElementById("write_subj").value = "Re: " + document.getElementById("write_subj").value;
436
			document.querySelector("#write2_pkey > input").value = isInt? ae.GetIntMsgFromPk(num) : "";
437
438
			document.getElementById("write_recv").readOnly = !isInt;
439
			document.getElementById("write_subj").readOnly = !isInt;
440
			document.getElementById("write_subj").setAttribute("data-replyid", isInt? "" : ae.GetExtMsgHdrId(num));
441
442
			tabs[TAB_WRITE].cur = 0;
443
			document.getElementById("btn_write").disabled = false;
444
			document.getElementById("btn_write").click();
445
			document.getElementById("write_body").focus();
446
447
			for (const opt of document.getElementById("write_from").options) {
448
				if (opt.value === (isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgEnvTo(num))) {
449
					opt.selected = true;
450
				}
451
			}
452
		};
453
	} else {
454
		document.getElementById("btn_reply").disabled = true;
455
	}
456
457
	document.getElementById("midright").children[0].hidden = false;
458
	document.getElementById("midright").children[2].hidden = false;
459
460
	document.getElementById("readmsg_envto").textContent = isInt ? "" : ae.GetExtMsgEnvTo(num);
461
	document.getElementById("readmsg_hdrto").textContent = isInt ? ae.GetIntMsgTo(num) : ae.GetExtMsgHdrTo(num);
462
463
	const tzOs = new Date().getTimezoneOffset();
464
	const msgDate = new Date((ts * 1000) + (tzOs * -60000));
465
	document.getElementById("readmsg_date").children[0].innerHTML = getClockIcon(msgDate);
466
467
	if (isInt) {
468
		document.getElementById("midright").children[1].textContent = ae.GetIntMsgTitle(num);
469
		document.getElementById("midright").children[2].textContent = ae.GetIntMsgBody(num);
470
471
		document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ");
472
473
		document.getElementById("readmsg_ip").style.visibility = "hidden";
474
		document.getElementById("readmsg_rdns").style.visibility = "hidden";
475
		document.getElementById("readmsg_dkim").style.visibility = "hidden";
476
		document.getElementById("readmsg_greet").style.visibility = "hidden";
477
		document.getElementById("readmsg_cert").style.visibility = "hidden";
478
		document.getElementById("readmsg_envfrom").style.visibility = "hidden";
479
		document.getElementById("readmsg_envto").style.visibility = "hidden";
480
481
		document.getElementById("readmsg_tls").style.visibility = "visible";
482
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetIntMsgFromPk(num);
483
484
		let symbol = "<span title=\"Invalid level\">&#x26a0;</span>";
485
		if      (ae.GetIntMsgFrom(num) === "system") {if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"System message\">&#x1f162;</span>";} // (S)
486
		else if (ae.GetIntMsgFrom(num) === "public") {if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"Public announcement\">&#x1f15f;</span>";} // (P)
487
		else if (ae.GetIntMsgLevel(num) === 0) symbol = "<span title=\"Level 0 User\">&#x1f10c;</span>"; // 0
488
		else if (ae.GetIntMsgLevel(num) === 1) symbol = "<span title=\"Level 1 User\">&#x278a;</span>"; // 1
489
		else if (ae.GetIntMsgLevel(num) === 2) symbol = "<span title=\"Level 2 User\">&#x278b;</span>"; // 2
490
		else if (ae.GetIntMsgLevel(num) === 3) symbol = "<span title=\"Administrator\">&#x1f150;</span>"; // A (Admin)
491
		document.getElementById("readmsg_hdrfrom").innerHTML = symbol + " " + ae.GetIntMsgFrom(num);
492
493
		let flagText = "";
494
		if (!ae.GetIntMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
495
		if (!ae.GetIntMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
496
		if (ae.GetIntMsgFlagE2ee(num)) flagText += "<abbr title=\"End-to-end encrypted\">E2EE</abbr> ";
497
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
498
	} else {
499
		document.getElementById("midright").children[2].innerHTML = "";
500
501
		const headers = document.createElement("p");
502
		headers.textContent = ae.GetExtMsgHeaders(num);
503
		headers.className = "mono";
504
		headers.hidden = !showHeaders;
505
		document.getElementById("midright").children[2].appendChild(headers);
506
507
		const certText = document.getElementById("readmsg_cert");
508
509
		let names = [];
510
		if (ae.GetExtMsgTls_MatchGreeting(num)) names.push(ae.GetExtMsgGreet(num));
511
		if (ae.GetExtMsgTls_MatchRdns(num))     names.push(ae.GetExtMsgRdns(num));
512
		if (ae.GetExtMsgTls_MatchEnvFrom(num))  names.push(ae.GetExtMsgEnvFrom(num).split("@")[1]);
513
		if (ae.GetExtMsgTls_MatchHdrFrom(num))  names.push(ae.GetExtMsgHdrFrom(num).split("@")[1]);
514
		names = [...new Set(names)];
515
516
		certText.children[0].textContent = (names[0]) ? (names.join(" ") + " " + ae.GetExtMsgTls_CertType(num)).trim() : ae.GetExtMsgTls_CertType(num);
517
518
		const body = document.createElement("p");
519
		body.textContent = ae.GetExtMsgBody(num);
520
		document.getElementById("midright").children[2].appendChild(body);
521
522
		document.getElementById("midright").children[1].textContent = ae.GetExtMsgTitle(num);
523
		document.getElementById("midright").children[1].onclick = function() {showHeaders = !showHeaders; headers.hidden = !showHeaders;};
524
		document.getElementById("midright").children[1].style.cursor = "pointer";
525
526
		let hdrSecs = Math.abs(ae.GetExtMsgHdrTime(num));
527
		let hdrTime = "";
528
		if (hdrSecs >= 3600) {
529
			const hdrHours = Math.floor(hdrSecs / 3600);
530
			hdrTime += hdrHours.toString() + "h ";
531
			hdrSecs -= hdrHours * 3600;
532
		}
533
		if (hdrSecs >= 60) {
534
			const hdrMins = Math.floor(hdrSecs / 60);
535
			hdrTime += hdrMins.toString() + "m ";
536
			hdrSecs -= hdrMins * 60;
537
		}
538
		hdrTime += hdrSecs + "s";
539
540
		const hdrTz = (ae.GetExtMsgHdrTz(num) >= 0 ? "+" : "-") + Math.floor(Math.abs(ae.GetExtMsgHdrTz(num)) / 60).toString().padStart(2, "0") + (Math.abs(ae.GetExtMsgHdrTz(num)) % 60).toString().padStart(2, "0");
541
		document.getElementById("readmsg_date").children[1].textContent = msgDate.toISOString().slice(0, 19).replace("T", " ") + "; " + hdrTz + " " + ((ae.GetExtMsgHdrTime(num) >= 0) ? "+" : "-") + hdrTime;
542
543
		document.getElementById("readmsg_ip").style.visibility = "visible";
544
		document.getElementById("readmsg_rdns").style.visibility = "visible";
545
		document.getElementById("readmsg_dkim").style.visibility = "visible";
546
		document.getElementById("readmsg_greet").style.visibility = "visible";
547
		document.getElementById("readmsg_cert").style.visibility = "visible";
548
		document.getElementById("readmsg_envfrom").style.visibility = "visible";
549
		document.getElementById("readmsg_envto").style.visibility = "visible";
550
551
		const cc = ae.GetExtMsgCountry(num);
552
553
		document.getElementById("readmsg_country").textContent = getCountryFlag(cc);
554
		document.getElementById("readmsg_country").title = getCountryName(cc);
555
		document.getElementById("readmsg_ip").children[1].textContent = ae.GetExtMsgIp(num);
556
		document.getElementById("readmsg_rdns").children[0].textContent = ae.GetExtMsgRdns(num);
557
		document.getElementById("readmsg_tls").children[0].textContent = ae.GetExtMsgTLS(num);
558
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetExtMsgGreet(num);
559
		document.getElementById("readmsg_envfrom").textContent = ae.GetExtMsgEnvFrom(num);
560
		document.getElementById("readmsg_hdrfrom").textContent = ae.GetExtMsgHdrFrom(num);
561
562
		let flagText = "";
563
		if (!ae.GetExtMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
564
		if (!ae.GetExtMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
565
		if (!ae.GetExtMsgFlagPExt(num)) flagText += "<abbr title=\"The sender did not use the Extended (ESMTP) protocol\">SMTP</abbr> ";
566
		if (!ae.GetExtMsgFlagQuit(num)) flagText += "<abbr title=\"The sender did not issue the required QUIT command\">QUIT</abbr> ";
567
		if (ae.GetExtMsgFlagRare(num)) flagText += "<abbr title=\"The sender issued unusual command(s)\">RARE</abbr> ";
568
		if (ae.GetExtMsgFlagFail(num)) flagText += "<abbr title=\"The sender issued invalid command(s)\">FAIL</abbr> ";
569
		if (ae.GetExtMsgFlagPErr(num)) flagText += "<abbr title=\"The sender violated the protocol\">PROT</abbr> ";
570
		if (ae.GetExtMsgFlagMult(num)) flagText += "<abbr title=\"Multiple recipients on this service\">MULT</abbr> ";
571
		document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
572
	}
573
}
574
575
// Interface
576
function addMsg(isInt, i) {
577
	const row = document.getElementById("tbl_inbox").insertRow(-1);
578
	row.setAttribute("data-msgid", isInt? ae.GetIntMsgIdHex(i) : ae.GetExtMsgIdHex(i));
579
580
	const cellTime = row.insertCell(-1);
581
	const ts = isInt? ae.GetIntMsgTime(i) : ae.GetExtMsgTime(i);
582
	cellTime.setAttribute("data-ts", ts);
583
	cellTime.textContent = new Date((ts * 1000) + (new Date().getTimezoneOffset() * -60000)).toISOString().slice(0, 10);
584
585
	const cellSubj = row.insertCell(-1);
586
	cellSubj.textContent = isInt? ae.GetIntMsgTitle(i) : ae.GetExtMsgTitle(i);
587
588
	if (isInt) {
589
		const cellSnd = row.insertCell(-1);
590
		cellSnd.textContent = ae.GetIntMsgFrom(i);
591
		cellSnd.className = (ae.GetIntMsgFrom(i).length === 16) ? "mono" : "";
592
	} else {
593
		const from1 = ae.GetExtMsgEnvFrom(i);
594
		const from2 = from1.substring(from1.indexOf("@") + 1);
595
		const cc = ae.GetExtMsgCountry(i);
596
		const cellSnd1 = row.insertCell(-1);
597
		cellSnd1.textContent = from1.substring(0, from1.indexOf("@"));
598
599
		const flag = document.createElement("abbr");
600
		flag.textContent = getCountryFlag(cc);
601
		flag.title = getCountryName(cc);
602
603
		const fromText = document.createElement("span");
604
		fromText.textContent = " " + from2;
605
606
		const cellSnd2 = row.insertCell(-1);
607
		cellSnd2.appendChild(flag);
608
		cellSnd2.appendChild(fromText);
609
	}
610
611
	row.onclick = function() {
612
		displayMsg(isInt, i);
613
	};
614
}
615
616
function getRowsPerPage() {
617
	const tbl = document.getElementById("tbl_inbox");
618
	tbl.innerHTML = "";
619
	const row = tbl.insertRow(-1);
620
	const cell = row.insertCell(-1);
621
	cell.textContent = "0";
622
623
	const rowsPerPage = Math.floor(getComputedStyle(document.getElementById("div_inbox")).height.replace("px", "") / getComputedStyle(document.querySelector("#tbl_inbox > tbody > tr:first-child")).height.replace("px", "")) - 1; // -1 allows space for 'load more'
624
	tbl.innerHTML = "";
625
	return rowsPerPage;
626
}
627
628
function addMessages() {
629
	const maxExt = ae.GetExtMsgCount();
630
	const maxInt = ae.GetIntMsgCount();
631
632
	if (maxExt + maxInt < 1) {
633
		tabs[TAB_INBOX].max = 0;
634
		return;
635
	}
636
637
	const rowsPerPage = getRowsPerPage();
638
	let skipMsgs = rowsPerPage * tabs[TAB_INBOX].cur;
639
640
	tabs[TAB_INBOX].max = Math.floor((maxExt + maxInt - 1) / rowsPerPage);
641
642
	let numExt = 0;
643
	let numInt = 0;
644
	let numAdd = 0;
645
646
	while (numAdd < rowsPerPage) {
647
		const tsInt = (numInt < maxInt) ? ae.GetIntMsgTime(numInt) : -1;
648
		const tsExt = (numExt < maxExt) ? ae.GetExtMsgTime(numExt) : -1;
649
		if (tsInt === -1 && tsExt === -1) break;
650
651
		if (tsInt !== -1 && (tsExt === -1 || tsInt > tsExt)) {
652
			if (skipMsgs > 0) skipMsgs--; else {addMsg(true, numInt); numAdd++;}
653
			numInt++;
654
		} else if (tsExt !== -1) {
655
			if (skipMsgs > 0) skipMsgs--; else {addMsg(false, numExt); numAdd++;}
656
			numExt++;
657
		}
658
	}
659
660
	if (ae.GetReadyMsgBytes() < ae.GetTotalMsgBytes()) {
661
		const inbox = document.getElementById("tbl_inbox");
662
		const row = inbox.insertRow(-1);
663
		const cell = row.insertCell(-1);
664
		cell.textContent = "Load more (" + Math.round((ae.GetTotalMsgBytes() - ae.GetReadyMsgBytes()) / 1024) + " KiB left)";
665
666
		row.onclick = function() {
667
			this.onclick = "";
668
669
			ae.Message_Browse(false, false, function(successBrowse) {
670
				document.getElementById("tbl_inbox").style.opacity = 1;
671
672
				if (successBrowse) {
673
					addMessages();
674
					addUploads();
675
					addSent();
676
					if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
677
				}
678
			});
679
		};
680
	}
681
}
682
683
function addUploads() {
684
	const tbl = document.getElementById("tbd_uploads");
685
	tbl.innerHTML = "";
686
687
	for (let i = 0; i < ae.GetUplMsgCount(); i++) {
688
		const row = tbl.insertRow(-1);
689
		row.setAttribute("data-msgid", ae.GetUplMsgIdHex(i));
690
691
		let cell;
692
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetUplMsgTime(i) * 1000).toISOString().slice(0, 10);
693
694
		cell = row.insertCell(-1); cell.textContent = ae.GetUplMsgTitle(i);
695
		cell.onclick = function() {displayFile(this.parentElement.rowIndex - 1);};
696
697
		cell = row.insertCell(-1); cell.textContent = (ae.GetUplMsgBytes(i) / 1024).toFixed(1);
698
699
		cell = row.insertCell(-1);
700
		if (ae.GetUplMsgIdHex(i)) {
701
			cell.innerHTML = "<button data-msgid=\"" + ae.GetUplMsgIdHex(i) + "\" type=\"button\">X</button>";
702
703
			cell.children[0].onclick = function() {
704
				const tr = this.parentElement.parentElement;
705
				ae.Message_Delete(this.getAttribute("data-msgid"), function(success) {
706
					if (success) tr.remove();
707
				});
708
			};
709
		}
710
	}
711
}
712
713
function displayOutMsg(num) {
714
	clearDisplay();
715
	document.getElementById("midright").scroll(0, 0);
716
	document.getElementById("midright").setAttribute("data-msgid", ae.GetOutMsgIdHex(num));
717
	document.getElementById("btn_reply").disabled = true;
718
	document.getElementById("btn_mdele").disabled = false;
719
	document.getElementById("midright").children[0].hidden = false;
720
	document.getElementById("midright").children[2].hidden = false;
721
722
	document.getElementById("midright").children[1].textContent = ae.GetOutMsgSubj(num);
723
	document.getElementById("midright").children[2].textContent = ae.GetOutMsgBody(num);
724
725
	document.getElementById("readmsg_dkim").style.visibility    = "hidden";
726
	document.getElementById("readmsg_hdrto").style.visibility   = "visible";
727
	document.getElementById("readmsg_hdrfrom").style.visibility = "visible";
728
	document.getElementById("readmsg_envto").style.visibility   = "visible";
729
	document.getElementById("readmsg_envfrom").style.visibility = "hidden";
730
731
	document.getElementById("readmsg_hdrfrom").textContent = ae.GetOutMsgFrom(num);
732
733
	document.getElementById("readmsg_envto").textContent = ae.GetOutMsgMxDom(num);
734
	document.getElementById("readmsg_hdrto").textContent = ae.GetOutMsgTo(num);
735
736
	const ts = ae.GetOutMsgTime(num);
737
	const tzOs = new Date().getTimezoneOffset();
738
	document.getElementById("readmsg_date").children[1].textContent = new Date((ts * 1000) + (tzOs * -60000)).toISOString().slice(0, 19).replace("T", " ");
739
740
	const isInt = ae.GetOutMsgIsInt(num);
741
	document.getElementById("readmsg_ip").style.visibility    = isInt? "hidden" : "visible";
742
	document.getElementById("readmsg_rdns").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
743
	document.getElementById("readmsg_tls").style.visibility   = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
744
	document.getElementById("readmsg_cert").style.visibility  = /*isInt?*/ "hidden" /*: "visible"*/; // TODO
745
	document.getElementById("readmsg_greet").style.visibility = isInt? "hidden" : "visible";
746
747
	if (!isInt) {
748
//		const cc = ae.GetExtMsgCountry(num);
749
750
		document.getElementById("readmsg_ip").children[1].textContent = ae.GetOutMsgIp(num);
751
//		document.getElementById("readmsg_country").textContent = getCountryFlag(cc) + " " + getCountryName(cc);
752
//		document.getElementById("readmsg_tls").children[0].textContent = ae.GetOutMsgTLS(num);
753
		document.getElementById("readmsg_greet").children[0].textContent = ae.GetOutMsgGreet(num);
754
	}
755
756
	let flagText = "";
757
	if (!ae.GetOutMsgFlagVPad(num)) flagText += "<abbr title=\"Invalid padding\">PAD</abbr> ";
758
	if (!ae.GetOutMsgFlagVSig(num)) flagText += "<abbr title=\"Invalid signature\">SIG</abbr> ";
759
	if (ae.GetOutMsgFlagE2ee(num)) flagText += "<abbr title=\"End-to-end encrypted\">E2EE</abbr> ";
760
	document.getElementById("readmsg_flags").children[0].innerHTML = flagText.trim();
761
}
762
763
function addSent() {
764
	const tbl = document.getElementById("tbl_drbox");
765
	tbl.innerHTML = "";
766
767
	for (let i = 0; i < ae.GetOutMsgCount(); i++) {
768
		const row = tbl.insertRow(-1);
769
		row.setAttribute("data-msgid", ae.GetOutMsgIdHex(i));
770
771
		let cell;
772
		cell = row.insertCell(-1); cell.textContent = new Date(ae.GetOutMsgTime(i) * 1000).toISOString().slice(0, 10);
773
		cell = row.insertCell(-1); cell.textContent = ae.GetOutMsgSubj(i);
774
		row.onclick = function() {displayOutMsg(i);};
775
	}
776
}
777
778
function updateAddressCounts() {
779
	document.getElementById("limit_normal").textContent = (ae.GetAddressCountNormal() + "/" + ae.GetLimitNormalA(ae.GetUserLevel())).padStart(ae.GetLimitNormalA(ae.GetUserLevel()) > 9 ? 5 : 1);
780
	document.getElementById("limit_shield").textContent = (ae.GetAddressCountShield() + "/" + ae.GetLimitShieldA(ae.GetUserLevel())).padStart(ae.GetLimitShieldA(ae.GetUserLevel()) > 9 ? 5 : 1);
781
	document.getElementById("limit_total").textContent = ((ae.GetAddressCountNormal() + ae.GetAddressCountShield()) + "/" + ae.GetAddrPerUser()).padStart(5);
782
783
	const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
784
	document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()));
785
	document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()));
786
}
787
788
function adjustLevel(pubkey, level, c) {
789
	const fs = document.getElementById("fs_accs");
790
	fs.disabled = true;
791
792
	ae.Account_Update(pubkey, level, function(success) {
793
		fs.disabled = false;
794
795
		if (success) {
796
			c[4].textContent = level;
797
			c[5].children[0].disabled = (level === 3);
798
			c[6].children[0].disabled = (level === 0);
799
		}
800
	});
801
}
802
803
function addAccountToTable(i) {
804
	const tblAccs = document.getElementById("tbd_accs");
805
	const row = tblAccs.insertRow(-1);
806
	let cell;
807
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserPkHex(i);
808
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSpace(i);
809
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserNAddr(i);
810
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserSAddr(i);
811
	cell = row.insertCell(-1); cell.textContent = ae.Admin_GetUserLevel(i);
812
813
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">+</button>";
814
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) + 1, c);};
815
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 3);
816
817
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">&minus;</button>";
818
	cell.children[0].onclick = function() {const c = this.parentElement.parentElement.cells; adjustLevel(c[0].textContent, parseInt(c[4].textContent, 10) - 1, c);};
819
	cell.children[0].disabled = (ae.Admin_GetUserLevel(i) === 0);
820
821
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\">X</button>";
822
	cell.children[0].onclick = function() {
823
		const tr = this.parentElement.parentElement;
824
		ae.Account_Delete(tr.cells[0].textContent, function(success) {
825
			if (success) tr.remove();
826
		});
827
	};
828
}
829
830
function reloadAccount() {
831
	// Limits
832
	const tblLimits = document.getElementById("tbl_limits");
833
	if (ae.IsUserAdmin()) {
834
		for (let i = 0; i < 4; i++) {
835
			tblLimits.rows[i].cells[1].children[0].disabled = false;
836
			tblLimits.rows[i].cells[2].children[0].disabled = false;
837
			tblLimits.rows[i].cells[3].children[0].disabled = false;
838
839
			tblLimits.rows[i].cells[1].children[0].value = ae.GetLimitStorage(i) + 1;
840
			tblLimits.rows[i].cells[2].children[0].value = ae.GetLimitNormalA(i);
841
			tblLimits.rows[i].cells[3].children[0].value = ae.GetLimitShieldA(i);
842
		}
843
	} else {
844
		const lvl = ae.GetUserLevel();
845
		tblLimits.rows[lvl].cells[1].children[0].value = ae.GetLimitStorage(lvl) + 1;
846
		tblLimits.rows[lvl].cells[2].children[0].value = ae.GetLimitNormalA(lvl);
847
		tblLimits.rows[lvl].cells[3].children[0].value = ae.GetLimitShieldA(lvl);
848
	}
849
850
	// Accounts
851
	const tblAccs = document.getElementById("tbd_accs");
852
853
	// All: Our account
854
	const row = tblAccs.insertRow(-1);
855
	let cell;
856
	cell = row.insertCell(-1); cell.textContent = ae.GetUserPkHex();
857
	cell = row.insertCell(-1); cell.textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
858
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountNormal();
859
	cell = row.insertCell(-1); cell.textContent = ae.GetAddressCountShield();
860
	cell = row.insertCell(-1); cell.textContent = ae.GetUserLevel();
861
	cell = row.insertCell(-1); cell.innerHTML = "<button type=\"button\" autocomplete=\"off\" disabled=\"disabled\">+</button>";
862
863
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_downme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">&minus;</button>";
864
	cell.children[0].onclick = function() {
865
		const newLevel = parseInt(row.cells[4].textContent, 10) - 1;
866
		ae.Account_Update(ae.GetUserPkHex(), newLevel, function(success) {
867
			if (success) row.cells[4].textContent = newLevel;
868
		});
869
	};
870
871
	cell = row.insertCell(-1); cell.innerHTML = "<button id=\"btn_killme\" type=\"button\" autocomplete=\"off\" disabled=\"disabled\">X</button>";
872
	cell.children[0].onclick = function() {
873
		ae.Account_Delete(ae.GetUserPkHex(), function(success) {
874
			if (success) row.remove();
875
		});
876
	};
877
878
	document.getElementById("txt_reg").disabled = !ae.IsUserAdmin();
879
	document.getElementById("btn_reg").disabled = !ae.IsUserAdmin();
880
881
	// Contacts
882
	for (let i = 0; i < ae.GetContactCount(); i++) {
883
		addContact(
884
			ae.GetContactMail(i),
885
			ae.GetContactName(i),
886
			ae.GetContactNote(i)
887
		);
888
	}
889
890
	refreshContactList();
891
892
	// Addresses
893
	for (let i = 0; i < ae.GetAddressCount(); i++) {
894
		addAddress(i);
895
	}
896
897
	updateAddressCounts();
898
	addMessages();
899
	addUploads();
900
	addSent();
901
902
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
903
}
904
905
function deleteAddress(addr) {
906
	let btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
907
	for (let i = 0; i < btns.length; i++) btns[i].disabled = true;
908
909
	let addressToDelete = -1;
910
911
	for (let i = 0; i < ae.GetAddressCount(); i++) {
912
		if (addr === ae.GetAddress(i)) {
913
			addressToDelete = i;
914
			break;
915
		}
916
	}
917
918
	if (addressToDelete === -1) return;
919
920
	ae.Address_Delete(addressToDelete, function(success) {
921
		if (success) {
922
			document.getElementById("tbl_addrs").deleteRow(addressToDelete);
923
			document.getElementById("write_from").remove(addressToDelete);
924
			updateAddressCounts();
925
926
			const limitReached = (ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31);
927
			document.getElementById("btn_address_create_normal").disabled = (limitReached || ae.GetAddressCountNormal() > ae.GetLimitNormalA(ae.GetUserLevel()));
928
			document.getElementById("btn_address_create_shield").disabled = (limitReached || ae.GetAddressCountShield() > ae.GetLimitShieldA(ae.GetUserLevel()));
929
930
			ae.Private_Update(function(success2) {
931
				if (!success2) console.log("Failed to update the Private field");
932
933
				btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
934
				for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
935
			});
936
		} else {
937
			console.log("Failed to delete address");
938
939
			btns = document.getElementById("tbl_addrs").getElementsByTagName("button");
940
			for (let i = 0; i < btns.length; i++) btns[i].disabled = false;
941
		}
942
	});
943
}
944
945 View Code Duplication
function shieldMix(addr) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
946
	let newAddr = "";
947
948
	for (let i = 0; i < 16; i++) {
949
		switch (addr.charAt(i)) {
950
			case '1':
951
				newAddr += "1iIlL".charAt(Math.floor(Math.random() * 5));
952
				break;
953
			case '0':
954
				newAddr += "0oO".charAt(Math.floor(Math.random() * 3));
955
				break;
956
			case 'w':
957
				newAddr += "VvWw".charAt(Math.floor(Math.random() * 4));
958
				break;
959
			default:
960
				newAddr += (Math.random() > 0.5) ? addr.charAt(i) : addr.charAt(i).toUpperCase();
961
		}
962
	}
963
964
	return newAddr;
965
}
966
967
function addAddress(num) {
968
	const addrTable = document.getElementById("tbl_addrs");
969
	const row = addrTable.insertRow(-1);
970
	const cellAddr = row.insertCell(-1);
971
	const cellChk1 = row.insertCell(-1);
972
	const cellChk2 = row.insertCell(-1);
973
	const cellBtnD = row.insertCell(-1);
974
975
	cellAddr.textContent = ae.GetAddress(num);
976
	cellAddr.onclick = function() {
977
		if (cellAddr.textContent.length === 16)
978
			navigator.clipboard.writeText(shieldMix(cellAddr.textContent) + "@" + ae.GetDomainEml());
1 ignored issue
show
Bug introduced by
The variable navigator seems to be never declared. If this is a global, consider adding a /** global: navigator */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
979
		else
980
			navigator.clipboard.writeText(cellAddr.textContent + "@" + ae.GetDomainEml());
981
	};
982
983
	cellChk1.innerHTML = ae.GetAddressAccExt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
984
	cellChk2.innerHTML = ae.GetAddressAccInt(num) ? "<input type=\"checkbox\" checked=\"checked\">" : "<input type=\"checkbox\">";
985
986
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
987
	cellBtnD.onclick = function() {deleteAddress(cellAddr.textContent);};
988
989
	const opt = document.createElement("option");
990
	opt.value = cellAddr.textContent;
991
	opt.textContent = cellAddr.textContent + "@" + ae.GetDomainEml();
992
	document.getElementById("write_from").appendChild(opt);
993
}
994
995
document.getElementById("btn_dele").onclick = function() {
996
	this.blur();
997
998
	if (tab === TAB_WRITE) {
999
		tabs[tab].cur = 0;
1000
		updateTab();
1001
1002
		document.getElementById("write2_pkey").children[0].value = "";
1003
1004
		document.getElementById("write_recv").value = "";
1005
		document.getElementById("write_subj").value = "";
1006
		document.getElementById("write_body").value = "";
1007
1008
		document.getElementById("write_recv").readOnly = false;
1009
		document.getElementById("write_subj").readOnly = false;
1010
		document.getElementById("write_subj").setAttribute("data-replyid", "");
1011
1012
		document.getElementById("write_recv").focus();
1013
	}
1014
};
1015
1016
document.getElementById("btn_updt").onclick = function() {
1017
	const btn = this;
1018
	btn.disabled = true;
1019
	btn.blur();
1020
1021
	if (tab === TAB_INBOX) {
1022
		document.getElementById("tbl_inbox").style.opacity = 0.5;
1023
1024
		ae.Message_Browse(true, false, function(successBrowse) {
1025
			document.getElementById("tbl_inbox").style.opacity = 1;
1026
1027
			if (successBrowse) {
1028
				addMessages();
1029
				addUploads();
1030
				btn.disabled = false;
1031
			} else {
1032
				console.log("Failed to refresh");
1033
				btn.disabled = false;
1034
			}
1035
		});
1036
	}
1037
};
1038
1039
document.getElementById("btn_mdele").onclick = function() {
1040
	const btn = this;
1041
	btn.blur();
1042
	btn.disabled = true;
1043
1044
	const delId = document.getElementById("midright").getAttribute("data-msgid");
1045
	if (!delId) return;
1046
1047
	ae.Message_Delete(delId, function(success) {
1048
		if (success) {
1049
			["tbl_inbox", "tbl_drbox", "tbd_uploads"].forEach(function(tbl_name) {
1050
				const tbl = document.getElementById(tbl_name);
1051
				for (let i = 0; i < tbl.rows.length; i++) {if (tbl.rows[i].getAttribute("data-msgid") === delId) tbl.deleteRow(i);}
1052
			});
1053
1054
			addMessages();
1055
			addUploads();
1056
			addSent();
1057
		} else btn.disabled = false;
1058
	});
1059
};
1060
1061
function refreshContactList(mail, name, note) {
0 ignored issues
show
Unused Code introduced by
The parameter name is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter note is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
Unused Code introduced by
The parameter mail is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1062
	const lst = document.getElementById("contact_emails");
1063
	lst.innerHTML = "";
1064
1065
	for (let i = 0; i < ae.GetContactCount(); i++) {
1066
		const el = document.createElement("option");
1067
		el.value = ae.GetContactMail(i);
1068
		lst.appendChild(el);
1069
	}
1070
}
1071
1072
function addContact(mail, name, note) {
1073
	const tbl = document.getElementById("tbl_ctact");
1074
	const row = tbl.insertRow(-1);
1075
	const cellMail = row.insertCell(-1);
1076
	const cellName = row.insertCell(-1);
1077
	const cellNote = row.insertCell(-1);
1078
	const cellBtnD = row.insertCell(-1);
1079
1080
	cellMail.textContent = mail;
1081
	cellName.textContent = name;
1082
	cellNote.textContent = note;
1083
	cellBtnD.innerHTML = "<button type=\"button\">X</button>";
1084
1085
	cellMail.contentEditable = true;
1086
	cellName.contentEditable = true;
1087
	cellNote.contentEditable = true;
1088
1089
	cellBtnD.onclick = function() {row.remove();};
1090
}
1091
1092
document.getElementById("btn_newcontact").onclick = function() {
1093
	addContact("", "", "");
1094
};
1095
1096
document.getElementById("btn_savecontacts").onclick = function() {
1097
	while (ae.GetContactCount() > 0) {
1098
		ae.DeleteContact(0);
1099
	}
1100
1101
	for (const row of document.getElementById("tbl_ctact").rows) {
1102
		ae.AddContact(row.cells[0].textContent, row.cells[1].textContent, row.cells[2].textContent);
1103
	}
1104
1105
	refreshContactList();
1106
1107
	const btn = this;
1108
	btn.disabled = true;
1109
1110
	ae.Private_Update(function(success) {
1111
		btn.disabled = false;
1112
1113
		if (!success) {
1114
			console.log("Failed contacts update");
1115
		}
1116
	});
1117
};
1118
1119
function writeVerify() {
1120
	if (
1121
	   !document.getElementById("write_recv").reportValidity()
1122
	|| !document.getElementById("write_subj").reportValidity()
1123
	|| !document.getElementById("write_body").reportValidity()
1124
	) {tabs[TAB_WRITE].cur = 0; return;}
1125
1126
	document.getElementById("div_write_1").hidden = true;
1127
	document.getElementById("div_write_2").hidden = false;
1128
1129
	document.getElementById("write2_recv").textContent = document.getElementById("write_recv").value;
1130
	document.getElementById("write2_subj").textContent = document.getElementById("write_subj").value;
1131
	document.getElementById("write2_rply").textContent = document.getElementById("write_subj").getAttribute("data-replyid");
1132
	document.getElementById("write2_body").textContent = document.getElementById("write_body").value;
1133
1134
	if (document.getElementById("write_recv").value.indexOf("@") >= 0) {
1135
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value + "@" + ae.GetDomainEml();
1136
		document.getElementById("write2_pkey").hidden = true;
1137
	} else {
1138
		document.getElementById("write2_from").textContent = document.getElementById("write_from").value;
1139
		document.getElementById("write2_pkey").hidden = false;
1140
	}
1141
1142
	document.querySelector("#write2_send > button").disabled = false;
1143
	document.getElementById("write2_btntxt").textContent = (document.getElementById("write_recv").value === "public") ? "Make" : "Send to";
1144
}
1145
1146
function updateTab() {
1147
	switch (tab) {
1148
		case TAB_INBOX:
1149
			addMessages();
1150
		break;
1151
1152
		case TAB_DRBOX:
1153
			addSent();
1154
		break;
1155
1156
		case TAB_WRITE:
1157
			switch (tabs[tab].cur) {
1158
				case 0:
1159
					document.getElementById("div_write_1").hidden = false;
1160
					document.getElementById("div_write_2").hidden = true;
1161
					document.getElementById("write_body").focus();
1162
				break;
1163
1164
				case 1:
1165
					writeVerify();
1166
				break;
1167
			}
1168
		break;
1169
1170
		case TAB_NOTES:
1171
			for (let i = 0; i <= tabs[tab].max; i++) {
1172
				document.getElementById("div_notes").children[i].hidden = (i !== tabs[tab].cur);
1173
			}
1174
		break;
1175
1176
		case TAB_TOOLS:
1177
			for (let i = 0; i <= tabs[tab].max; i++) {
1178
				document.getElementById("div_tools").children[i].hidden = (i !== tabs[tab].cur);
1179
			}
1180
		break;
1181
	}
1182
1183
	document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
1184
	document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1185
}
1186
1187
document.getElementById("btn_left").onclick = function() {
1188
	tabs[tab].cur--;
1189
	if (tabs[tab].cur === 0) this.disabled = true;
1190
	if (tabs[tab].cur < tabs[tab].max) document.getElementById("btn_rght").disabled = false;
1191
	updateTab();
1192
	this.blur();
1193
};
1194
1195
document.getElementById("btn_rght").onclick = function() {
1196
	tabs[tab].cur++;
1197
	if (tabs[tab].cur === tabs[tab].max) this.disabled = true;
1198
	document.getElementById("btn_left").disabled = false;
1199
	updateTab();
1200
	this.blur();
1201
};
1202
1203
const buttons = document.querySelector("#main1 > .top").getElementsByTagName("button");
1204
for (let i = 0; i < buttons.length; i++) {
1205
	buttons[i].onclick = function() {
1206
		tab = i;
1207
1208
		for (let j = 0; j < buttons.length; j++) {
1209
			document.querySelector("#main1 > .mid").children[j].hidden = (tab !== j);
1210
			buttons[j].disabled = (tab === j);
1211
		}
1212
1213
		document.getElementById("btn_left").disabled = (tabs[tab].cur === 0);
0 ignored issues
show
Bug introduced by
The variable tab is changed as part of the for loop for example by i on line 1206. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
1214
		document.getElementById("btn_rght").disabled = (tabs[tab].cur === tabs[tab].max);
1215
		document.getElementById("btn_dele").disabled = !tabs[tab].btnDele;
1216
		document.getElementById("btn_updt").disabled = !tabs[tab].btnUpdt;
1217
1218
		updateTab();
1219
	};
1220
}
1221
1222
function addressCreate(addr) {
1223
	document.getElementById("btn_address_create_normal").disabled = true;
1224
	document.getElementById("btn_address_create_shield").disabled = true;
1225
1226
	ae.Address_Create(addr, function(success1) {
1227
		if (success1) {
1228
			ae.Private_Update(function(success2) {
1229
				updateAddressCounts();
1230
1231
				if (success2) {
1232
					addAddress(ae.GetAddressCount() - 1);
1233
					if (addr !== "SHIELD") document.getElementById("txt_address_create_normal").value = "";
1234
				} else {
1235
					console.log("Failed to update the Private field");
1236
				}
1237
			});
1238
		} else {
1239
			console.log("Failed to add address");
1240
			updateAddressCounts();
1241
		}
1242
	});
1243
}
1244
1245
document.getElementById("btn_address_create_normal").onclick = function() {
1246
	if (ae.GetAddressCountNormal() >= ae.GetLimitNormalA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1247
1248
	const txtNewAddr = document.getElementById("txt_address_create_normal");
1249
	if (!txtNewAddr.reportValidity()) return;
1250
1251
	addressCreate(txtNewAddr.value);
1252
};
1253
1254
document.getElementById("btn_address_create_shield").onclick = function() {
1255
	if (ae.GetAddressCountShield() >= ae.GetLimitShieldA(ae.GetUserLevel()) || ae.GetAddressCountNormal() + ae.GetAddressCountShield() >= 31) return;
1256
1257
	addressCreate("SHIELD");
1258
};
1259
1260
document.getElementById("btn_address_update").onclick = function() {
1261
	const btn = this;
1262
	btn.disabled = true;
1263
1264
	const rows = document.getElementById("tbl_addrs").rows;
1265
1266
	for (let i = 0; i < rows.length; i++) {
1267
		ae.SetAddressAccExt(i, rows[i].getElementsByTagName("input")[0].checked);
1268
		ae.SetAddressAccInt(i, rows[i].getElementsByTagName("input")[1].checked);
1269
	}
1270
1271
	ae.Address_Update(function(success) {
1272
		if (!success) console.log("Address/Update failed");
1273
		btn.disabled = false;
1274
	});
1275
};
1276
1277
document.getElementById("btn_reg").onclick = function() {
1278
	const btn = document.getElementById("btn_reg");
1279
	const txt = document.getElementById("txt_reg");
1280
	if (!txt.reportValidity()) return;
1281
	btn.disabled = true;
1282
1283
	ae.Account_Create(txt.value, function(success) {
1284
		if (success) {
1285
			addAccountToTable(ae.Admin_GetUserCount() - 1);
1286
			txt.value = "";
1287
		}
1288
1289
		btn.disabled = false;
1290
	});
1291
};
1292
1293
document.getElementById("chk_downme").onclick = function() {document.getElementById("btn_downme").disabled = !this.checked;};
1294
document.getElementById("chk_killme").onclick = function() {document.getElementById("btn_killme").disabled = !this.checked;};
1295
1296
document.getElementById("btn_notepad_saveupl").onclick = function() {
1297
	const np = document.getElementById("txt_notepad");
1298
	np.disabled = true;
1299
1300
	let fname = prompt("Save as...", "Untitled");
0 ignored issues
show
Debugging Code Best Practice introduced by
The prompt UI element is often considered obtrusive and is generally only used as a temporary measure. Consider replacing it with another UI element.
Loading history...
1301
	if (!fname.endsWith(".txt")) fname += ".txt";
1302
1303
	ae.Message_Upload(fname, np.value, function(success) {
1304
		if (success) {
1305
			np.value = "";
1306
			addUploads();
1307
			document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1308
		}
1309
1310
		console.log("Failed to add text");
1311
		np.disabled = false;
1312
	});
1313
};
1314
1315
document.getElementById("btn_upload").onclick = function() {
1316
	const btn = this;
1317
	const fileSelector = document.createElement("input");
1318
	fileSelector.type = "file";
1319
	fileSelector.click();
1320
1321
	fileSelector.onchange = function() {
1322
		btn.disabled = true;
1323
1324
		const reader = new FileReader();
1 ignored issue
show
Bug introduced by
The variable FileReader seems to be never declared. If this is a global, consider adding a /** global: FileReader */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1325
		reader.onload = function() {
1326
			ae.Message_Upload(fileSelector.files[0].name, new Uint8Array(reader.result), function(success) {
1327
				if (success) {
1328
					addUploads();
1329
					document.getElementById("tbd_accs").children[0].children[1].textContent = Math.round(ae.GetTotalMsgBytes() / 1024 / 1024);
1330
				} else {
1331
					console.log("Failed upload");
1332
				}
1333
1334
				btn.disabled = false;
1335
			});
1336
		};
1337
1338
		reader.readAsArrayBuffer(fileSelector.files[0]);
1339
	};
1340
};
1341
1342
document.getElementById("btn_pg").onclick = function() {
1343
	localStorage.greeting = document.getElementById("txt_pg").value;
1 ignored issue
show
Bug introduced by
The variable localStorage seems to be never declared. If this is a global, consider adding a /** global: localStorage */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1344
};
1345
1346
document.querySelector("#write2_send > button").onclick = function() {
1347
	const btn = this;
1348
	btn.disabled = true;
1349
1350
	// Public announcement
1351
	if (document.getElementById("write2_recv").textContent === "public") {
1352
		ae.Message_Public(document.getElementById("write_subj").value, document.getElementById("write_body").value, function(success) {
1353
			if (success) {
1354
				document.getElementById("write2_btntxt").textContent = "Announced to";
1355
				document.getElementById("write_recv").value = "";
1356
				document.getElementById("write_subj").value = "";
1357
				document.getElementById("write_body").value = "";
1358
			} else {
1359
				document.getElementById("write2_btntxt").textContent = "Retry making";
1360
				btn.disabled = false;
1361
			}
1362
		});
1363
1364
		return;
1365
	}
1366
1367
	// Email or internal message
1368
	document.getElementById("write2_btntxt").textContent = "Sending to";
1369
1370
	ae.Message_Create(
1371
		document.getElementById("write_subj").value,
1372
		document.getElementById("write_body").value,
1373
		document.getElementById("write_from").value,
1374
		document.getElementById("write_recv").value,
1375
		document.getElementById("write_subj").getAttribute("data-replyid"),
1376
		(document.getElementById("write2_recv").textContent.indexOf("@") > 0) ? null : sodium.from_base64(document.querySelector("#write2_pkey > input").value, sodium.base64_variants.ORIGINAL_NO_PADDING),
1 ignored issue
show
Bug introduced by
The variable sodium seems to be never declared. If this is a global, consider adding a /** global: sodium */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
1377
		function(success) {
1378
			if (success) {
1379
				document.getElementById("write2_btntxt").textContent = "Delivered to";
1380
				document.getElementById("write_recv").value = "";
1381
				document.getElementById("write_subj").value = "";
1382
				document.getElementById("write_body").value = "";
1383
			} else {
1384
				document.getElementById("write2_btntxt").textContent = "Retry sending to";
1385
				btn.disabled = false;
1386
			}
1387
		}
1388
	);
1389
};
1390
1391
document.getElementById("txt_skey").onkeyup = function(event) {
1392
	if (event.key === "Enter") {
1393
		event.preventDefault();
1394
		document.getElementById("btn_enter").click();
1395
	}
1396
};
1397
1398
document.getElementById("btn_enter").onclick = function() {
1399
	const txtSkey = document.getElementById("txt_skey");
1400
	if (!txtSkey.reportValidity()) return;
1401
1402
	const btn = this;
1403
	btn.disabled = true;
1404
	document.getElementById("txt_skey").style.background = "#233";
1405
1406
	ae.SetKeys(txtSkey.value, function(successSetKeys) {
1407
		if (successSetKeys) {
1408
			ae.Message_Browse(false, true, function(successBrowse) {
1409
				if (successBrowse) {
1410
					txtSkey.value = "";
1411
					document.getElementById("div_begin").hidden = true;
1412
					document.getElementById("div_main").style.display = "grid";
1413
					reloadAccount();
1414
1415
					if (ae.IsUserAdmin()) {
1416
						ae.Account_Browse(function(successAcc) {
1417
							if (successAcc) {for (let i = 0; i < ae.Admin_GetUserCount(); i++) {addAccountToTable(i);}}
1418
							else console.log("Failed to Account_Browse");
1419
						});
1420
					}
1421
				} else {
1422
					console.log("Failed to enter");
1423
					btn.disabled = false;
1424
					document.getElementById("txt_skey").style.background = "#466";
1425
					txtSkey.focus();
1426
				}
1427
			});
1428
		} else {
1429
			console.log("Invalid format for key");
1430
			btn.disabled = false;
1431
			document.getElementById("txt_skey").style.background = "#466";
1432
			txtSkey.focus();
1433
		}
1434
	});
1435
};
1436
1437
});
1438